from calendar import month
import pandas as pd
import datetime
import time
import pytz  # 用于时区处理
from okx.MarketData import MarketAPI
from typing import List, Optional, Dict, Any
from tqdm import tqdm
import logging
import os


class DataValidationError(Exception):
    """自定义异常类，用于数据校验失败时抛出"""
    pass


class OKXDataFetcher:
    def __init__(self, 
                 flag: str = "0",  # 实盘为"0"，模拟盘为"1"
                 default_inst_id: str = "DOGE-USDT",
                 max_retries: int = 3,  # 最大重试次数
                 retry_delay: int = 2):  # 重试延迟（秒）
        """初始化OKX数据获取器
        
        Args:
            flag: 交易环境标识，实盘为"0"，模拟盘为"1"
            default_inst_id: 默认交易对
            max_retries: 最大重试次数
            retry_delay: 重试延迟（秒）
        """
        self.market_api = MarketAPI(flag=flag)
        self.timezone = pytz.timezone('Asia/Shanghai')
        self.default_inst_id = default_inst_id
        self.default_bar = "1m"
        self.limit_per_request = 288
        self.time_delta_seconds = -60 * 288  # 时间偏移量（秒）
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        
        # 初始化日志
        self._setup_logger()
    
    def _setup_logger(self):
        """设置日志记录器"""
        # 创建logs目录
        log_dir = "./logs"
        os.makedirs(log_dir, exist_ok=True)
        
        # 创建日志文件名（按日期）
        today = datetime.date.today().strftime("%Y%m%d")
        log_file = os.path.join(log_dir, f"data_fetch_errors_{today}.log")
        
        # 配置日志记录器
        self.logger = logging.getLogger(f"OKXDataFetcher_{self.default_inst_id}")
        self.logger.setLevel(logging.ERROR)
        
        # 避免重复添加handler
        if not self.logger.handlers:
            # 文件处理器
            file_handler = logging.FileHandler(log_file, encoding='utf-8')
            file_handler.setLevel(logging.ERROR)
            
            # 日志格式
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S'
            )
            file_handler.setFormatter(formatter)
            
            self.logger.addHandler(file_handler)
    
    def _log_error(self, inst_id: str, year: int, month: int, day: int, 
                   error_type: str, error_message: str):
        """记录错误信息到日志文件
        
        Args:
            inst_id: 交易对
            year: 年份
            month: 月份
            day: 日期
            error_type: 错误类型
            error_message: 错误信息
        """
        log_message = f"交易对: {inst_id} | 日期: {year}-{month:02d}-{day:02d} | 错误类型: {error_type} | 错误信息: {error_message}"
        self.logger.error(log_message)
    
    def _validate_data(self, df: pd.DataFrame, year: int, month: int, day: int) -> None:
        """验证获取的数据是否符合要求
        
        Args:
            df: 待验证的DataFrame
            year: 预期年份
            month: 预期月份
            day: 预期日期
            
        Raises:
            DataValidationError: 数据验证失败时抛出
        """
        # 检查目标日期是否为当天或未来日期，如果是则数据不完整
        target_date = datetime.date(year, month, day)
        current_date = datetime.date.today()
        if target_date >= current_date:
            raise DataValidationError(f"无法获取当天或未来日期的完整数据，目标日期: {target_date}, 当前日期: {current_date}")
        
        if df.empty:
            raise DataValidationError("获取到的数据为空")
        
        # 虚拟币是24小时持续交易的，每天应该有1440条数据（每分钟一条）
        expected_records = 1440
        if len(df) != expected_records:
            raise DataValidationError(f"日期为{year}-{month}-{day}的数据记录数不正确，期望{expected_records}条，实际获取{len(df)}条")
        
        # 验证所有数据的日期是否都是指定的年月日
        # 提取日期部分并检查是否与目标日期一致
        df['date'] = df['datetime'].dt.date
        invalid_dates = df[df['date'] != target_date]['date'].unique()
        
        if len(invalid_dates) > 0:
            raise DataValidationError(
                f"数据包含非目标日期的数据，目标日期: {target_date}, "
                f"无效日期: {', '.join(map(str, invalid_dates))}"
            )
        
        # print(f"数据验证通过，获取到{len(df)}条记录")
        
        
    def generate_minute_timestamps(self, year: int, month: int, day: int) -> List[int]:
        """生成指定日期的分钟级时间戳列表，并按间隔筛选
        
        Args:
            year: 年份
            month: 月份
            day: 日期
            
        Returns:
            筛选后的时间戳列表（UTC时间戳）
        """
        try:
            # 验证日期有效性并添加时区信息
            naive_date = datetime.datetime(year, month, day)
            tz_date = self.timezone.localize(naive_date)  # 本地化日期到指定时区
        except ValueError as e:
            raise ValueError(f"无效的日期: {year}-{month}-{day}，错误: {str(e)}")
        
        timestamps = []
        for hour in range(24):
            for minute in range(60):
                try:
                    # 创建带有时区的datetime对象
                    naive_dt = datetime.datetime(year, month, day, hour, minute, 0)
                    tz_dt = self.timezone.localize(naive_dt)
                    
                    # 转换为UTC时间戳（秒）
                    timestamp = int(tz_dt.astimezone(pytz.UTC).timestamp()) - self.time_delta_seconds
                    timestamps.append(timestamp)
                except Exception as e:
                    print(f"生成时间戳时出错 {year}-{month}-{day} {hour}:{minute}: {str(e)}")
        
        # 按间隔筛选时间戳
        return [timestamps[i] for i in range(len(timestamps)) if i % self.limit_per_request == 0]
    
    def get_history_candlesticks(self, timestamp: int, 
                                inst_id: Optional[str] = None, 
                                bar: Optional[str] = None) -> Optional[pd.DataFrame]:
        """获取历史K线数据
        
        Args:
            timestamp: 时间戳
            inst_id: 交易对，默认为类初始化时的default_inst_id
            bar: K线周期，默认为类初始化时的default_bar
            
        Returns:
            包含K线数据的DataFrame，失败时返回None
        """
        # 使用默认值如果未提供
        inst_id = inst_id or self.default_inst_id
        bar = bar or self.default_bar
        
        for attempt in range(self.max_retries):
            try:
                # 调用API获取数据
                result = self.market_api.get_history_candlesticks(
                    instId=inst_id,
                    after=timestamp * 1000,  # 转换为毫秒
                    limit=self.limit_per_request,
                    bar=bar
                )
                
                # 检查API返回状态
                if not result or 'data' not in result:
                    print(f"API返回格式异常: {result}")
                    return None
                
                # 处理数据
                candlesticks = result['data']
                if not candlesticks:
                    print(f"未获取到K线数据，时间戳: {timestamp}")
                    return None
                
                # 转换为DataFrame，使用更具描述性的字段名
                df = pd.DataFrame(
                    candlesticks, 
                    columns=[
                        'timestamp_ms',       # 时间戳(毫秒)
                        'open',         # 开盘价
                        'high',         # 最高价
                        'low',          # 最低价
                        'close',        # 收盘价
                        'volume',             # 成交量(基础货币)
                        'volume_ccy',         # 成交量(计价货币)
                        'volume_ccy_quote',   # 成交量(报价货币)
                        'is_confirmed'        # 是否确认
                    ]
                )
                
                # 处理时间戳，转换为北京时间
                df['datetime'] = pd.to_datetime(df['timestamp_ms'].astype(int), unit='ms') + pd.Timedelta(hours=8)
                df.sort_values(by='timestamp_ms', inplace=True)
                
                # 删除timestamp_ms字段
                df = df.drop(columns=['timestamp_ms'])
                
                # 转换数值列的数据类型
                numeric_columns = [
                    'open', 'high', 'low', 'close',
                    'volume', 'volume_ccy', 'volume_ccy_quote', 'is_confirmed'
                ]
                for col in numeric_columns:
                    df[col] = pd.to_numeric(df[col], errors='coerce')
                
                return df
                
            except Exception as e:
                print(f"获取K线数据失败 (尝试 {attempt + 1}/{self.max_retries})，时间戳: {timestamp}，错误: {str(e)}")
                if attempt < self.max_retries - 1:
                    time.sleep(self.retry_delay)
        
        print(f"达到最大重试次数，获取K线数据失败，时间戳: {timestamp}")
        return None
    
    def fetch_data_for_day(self, year: int, month: int, day: int, 
                          inst_id: Optional[str] = None) -> pd.DataFrame:
        """获取指定日期的所有K线数据，并进行数据校验
        
        Args:
            year: 年份
            month: 月份
            day: 日期
            inst_id: 交易对，默认为类初始化时的default_inst_id
            
        Returns:
            合并后的K线数据DataFrame
            
        Raises:
            DataValidationError: 数据验证失败时抛出
        """
        # 使用默认交易对如果未提供
        inst_id = inst_id or self.default_inst_id
        
        try:
            # 生成时间戳
            timestamps = self.generate_minute_timestamps(year, month, day)
            if not timestamps:
                raise ValueError("未生成任何时间戳")
            
            # 获取并合并数据
            dataframe_list = []
            for index, timestamp in enumerate(timestamps):
                # print(f"正在获取数据: {index + 1}/{len(timestamps)}，时间戳: {timestamp}")
                dataframe = self.get_history_candlesticks(timestamp, inst_id)
                
                if dataframe is not None and not dataframe.empty:
                    dataframe_list.append(dataframe)
                
                # 添加请求间隔，避免触发API限制
                time.sleep(0.08)  # 每次请求间隔80毫秒
            
            if not dataframe_list:
                raise DataValidationError("未获取到任何有效数据")
            
            # 合并并去重
            combined_dataframe = pd.concat(dataframe_list).sort_values(by='datetime')
            combined_dataframe.drop_duplicates(subset=['datetime'], keep='last', inplace=True)
            
            # print(f"数据获取完成，共 {len(combined_dataframe)} 条记录")
            
            # 数据校验
            self._validate_data(combined_dataframe, year, month, day)
            
            return combined_dataframe
            
        except Exception as e:
            print(f"获取指定日期数据失败: {str(e)}")
            raise  # 重新抛出异常，让调用者知道发生了错误
    
    def generate_date_range(self, start_date: str, end_date: Optional[str] = None) -> List[datetime.date]:
        """生成从开始日期到结束日期的所有日期列表
        
        Args:
            start_date: 开始日期，格式为 'YYYY-MM-DD'
            end_date: 结束日期，格式为 'YYYY-MM-DD'，默认为今天
            
        Returns:
            日期列表
        """
        try:
            start = datetime.datetime.strptime(start_date, '%Y-%m-%d').date()
            today = datetime.date.today()
            
            if end_date:
                end = datetime.datetime.strptime(end_date, '%Y-%m-%d').date()
            else:
                # 默认到昨天，避免包含当天
                end = today - datetime.timedelta(days=1)
            
            # 确保结束日期不超过昨天
            if end >= today:
                end = today - datetime.timedelta(days=1)
                print(f"警告: 结束日期已调整为昨天 {end}，避免包含当天或未来日期")
            
            if start > end:
                raise ValueError(f"开始日期 {start} 不能晚于结束日期 {end}")
            
            date_list = []
            current_date = start
            while current_date <= end:
                date_list.append(current_date)
                current_date += datetime.timedelta(days=1)
            
            return date_list
            
        except ValueError as e:
            print(f"日期格式错误: {str(e)}")
            raise
    
    def fetch_historical_data_range(self, start_date: str, end_date: Optional[str] = None, 
                                   inst_id: Optional[str] = None, skip_existing: bool = True) -> None:
        """获取指定日期范围内的所有历史数据
        
        Args:
            start_date: 开始日期，格式为 'YYYY-MM-DD'
            end_date: 结束日期，格式为 'YYYY-MM-DD'，默认为今天
            inst_id: 交易对，默认为类初始化时的default_inst_id
            skip_existing: 是否跳过已存在的文件，默认为True
        """
        # 使用默认交易对如果未提供
        inst_id = inst_id or self.default_inst_id
        
        try:
            # 生成日期范围
            date_list = self.generate_date_range(start_date, end_date)
            total_days = len(date_list)
            
            print(f"开始获取 {inst_id} 从 {start_date} 到 {end_date or '今天'} 的历史数据")
            print(f"总共需要获取 {total_days} 天的数据")
            
            success_count = 0
            skip_count = 0
            error_count = 0
            
            # 使用tqdm创建进度条
            with tqdm(date_list, desc=f"获取 {inst_id} 历史数据", 
                     unit="天", ncols=100, colour='green') as pbar:
                
                for date in pbar:
                    year, month, day = date.year, date.month, date.day
                    filename = f'./datas/{inst_id}/{year:04d}/{month:02d}/{year:04d}{month:02d}{day:02d}.csv'
                    
                    # 更新进度条描述
                    pbar.set_description(f"处理 {year}-{month:02d}-{day:02d}")
                    
                    # 检查文件是否已存在
                    if skip_existing and self._file_exists(filename):
                        pbar.set_postfix(状态="跳过", 成功=success_count, 跳过=skip_count+1, 失败=error_count)
                        skip_count += 1
                        continue
                    
                    try:
                        pbar.set_postfix(状态="获取中", 成功=success_count, 跳过=skip_count, 失败=error_count)
                        data = self.fetch_data_for_day(year, month, day, inst_id)
                        
                        # 确保目录存在
                        import os
                        os.makedirs(os.path.dirname(filename), exist_ok=True)
                        
                        # 保存数据
                        data.to_csv(filename, index=False)
                        success_count += 1
                        pbar.set_postfix(状态="完成", 成功=success_count, 跳过=skip_count, 失败=error_count)
                        
                    except DataValidationError as e:
                        error_count += 1
                        pbar.set_postfix(状态="校验失败", 成功=success_count, 跳过=skip_count, 失败=error_count)
                        error_msg = str(e)
                        tqdm.write(f"数据校验失败 {year}-{month:02d}-{day:02d}: {error_msg}")
                        # 记录到日志文件
                        self._log_error(inst_id, year, month, day, "数据校验失败", error_msg)
                        
                        # 如果是当天或未来日期的错误，退出程序
                        if "无法获取当天或未来日期的完整数据" in error_msg:
                            tqdm.write(f"\n检测到当天或未来日期，停止数据获取。")
                            tqdm.write(f"最后处理日期: {year}-{month:02d}-{day:02d}")
                            break
                        continue
                    except Exception as e:
                        error_count += 1
                        pbar.set_postfix(状态="获取失败", 成功=success_count, 跳过=skip_count, 失败=error_count)
                        error_msg = str(e)
                        tqdm.write(f"获取数据失败 {year}-{month:02d}-{day:02d}: {error_msg}")
                        # 记录到日志文件
                        self._log_error(inst_id, year, month, day, "数据获取失败", error_msg)
                        continue
            
            print(f"\n数据获取完成！")
            print(f"成功: {success_count} 天")
            print(f"跳过: {skip_count} 天")
            print(f"失败: {error_count} 天")
            print(f"总计: {total_days} 天")
            
        except Exception as e:
            print(f"获取历史数据范围失败: {str(e)}")
            raise
    
    def _file_exists(self, filepath: str) -> bool:
        """检查文件是否存在
        
        Args:
            filepath: 文件路径
            
        Returns:
            文件是否存在
        """
        import os
        return os.path.exists(filepath)
    
    def check_missing_data(self, start_date: str, end_date: Optional[str] = None, 
                          inst_id: Optional[str] = None) -> List[datetime.date]:
        """检查指定日期范围内缺失的数据文件
        
        Args:
            start_date: 开始日期，格式为 'YYYY-MM-DD'
            end_date: 结束日期，格式为 'YYYY-MM-DD'，默认为今天
            inst_id: 交易对，默认为类初始化时的default_inst_id
            
        Returns:
            缺失数据的日期列表
        """
        # 使用默认交易对如果未提供
        inst_id = inst_id or self.default_inst_id
        
        # 生成完整的日期范围
        all_dates = self.generate_date_range(start_date, end_date)
        missing_dates = []
        
        for date in all_dates:
            year, month, day = date.year, date.month, date.day
            filename = f'./datas/{inst_id}/{year:04d}/{month:02d}/{year:04d}{month:02d}{day:02d}.csv'
            
            if not self._file_exists(filename):
                missing_dates.append(date)
        
        return missing_dates
    
    def test_data_availability(self, year: int, month: int, day: int, inst_id: Optional[str] = None) -> bool:
        """测试指定日期是否有数据可获取
        
        Args:
            year: 年份
            month: 月份
            day: 日期
            inst_id: 交易对，默认为类初始化时的default_inst_id
            
        Returns:
            是否有数据可获取
        """
        inst_id = inst_id or self.default_inst_id
        
        try:
            # 尝试获取该日期的数据，但不进行完整验证
            timestamps = self.generate_minute_timestamps(year, month, day)
            if not timestamps:
                return False
            
            # 只测试第一个时间戳
            dataframe = self.get_history_candlesticks(timestamps[0], inst_id)
            
            # 添加延迟避免API频率限制
            time.sleep(0.5)
            
            return dataframe is not None and not dataframe.empty
            
        except Exception as e:
            print(f"测试日期 {year}-{month:02d}-{day:02d} 数据可用性失败: {str(e)}")
            return False
    
    def find_data_range(self, start_year: int, end_year: Optional[int] = None, 
                       inst_id: Optional[str] = None) -> tuple:
        """查找交易对数据的实际可用日期范围
        
        Args:
            start_year: 开始年份
            end_year: 结束年份，默认为当前年份
            inst_id: 交易对，默认为类初始化时的default_inst_id
            
        Returns:
            (最早可用日期, 最晚可用日期) 的元组，如果没有数据则返回 (None, None)
        """
        inst_id = inst_id or self.default_inst_id
        end_year = end_year or datetime.date.today().year
        
        print(f"🔍 正在检测 {inst_id} 的数据可用范围 ({start_year}-{end_year})...")
        
        earliest_date = None
        latest_date = None
        
        # 从开始年份向后查找最早可用日期
        for year in range(start_year, end_year + 1):
            print(f"检测 {year} 年数据可用性...")
            
            # 先测试1月1日
            jan_1_available = self.test_data_availability(year, 1, 1, inst_id)
            # 再测试12月31日
            dec_31_available = self.test_data_availability(year, 12, 31, inst_id)
            
            if not jan_1_available and not dec_31_available:
                print(f"❌ {year} 年无数据，跳过")
                continue
            
            print(f"✅ {year} 年有数据，开始精确定位...")
            
            # 如果还没找到最早日期，进行二分查找
            if earliest_date is None:
                earliest_date = self._binary_search_earliest_date(year, inst_id)
                if earliest_date:
                    print(f"📅 找到最早可用日期: {earliest_date}")
            
            # 更新最晚日期
            if dec_31_available:
                latest_date = datetime.date(year, 12, 31)
            else:
                # 如果12月31日没数据，查找该年最后一个有数据的日期
                year_latest = self._binary_search_latest_date(year, inst_id)
                if year_latest:
                    latest_date = year_latest
        
        # 如果latest_date还是None，检查今天
        if latest_date is None and earliest_date is not None:
            today = datetime.date.today()
            if self.test_data_availability(today.year, today.month, today.day, inst_id):
                latest_date = today
        
        return earliest_date, latest_date
    
    def _binary_search_earliest_date(self, year: int, inst_id: str) -> Optional[datetime.date]:
        """在指定年份中二分查找最早的可用日期"""
        start_date = datetime.date(year, 1, 1)
        end_date = datetime.date(year, 12, 31)
        
        # 如果1月1日有数据，直接返回
        if self.test_data_availability(year, 1, 1, inst_id):
            return start_date
        
        # 二分查找
        left = 1  # 1月1日是第1天
        right = (end_date - start_date).days + 1  # 该年总天数
        
        result_date = None
        
        while left <= right:
            mid = (left + right) // 2
            test_date = start_date + datetime.timedelta(days=mid - 1)
            
            if self.test_data_availability(test_date.year, test_date.month, test_date.day, inst_id):
                result_date = test_date
                right = mid - 1  # 继续向前查找更早的日期
            else:
                left = mid + 1
        
        return result_date
    
    def _binary_search_latest_date(self, year: int, inst_id: str) -> Optional[datetime.date]:
        """在指定年份中二分查找最晚的可用日期"""
        start_date = datetime.date(year, 1, 1)
        end_date = datetime.date(year, 12, 31)
        
        # 如果12月31日有数据，直接返回
        if self.test_data_availability(year, 12, 31, inst_id):
            return end_date
        
        # 二分查找
        left = 1
        right = (end_date - start_date).days + 1
        
        result_date = None
        
        while left <= right:
            mid = (left + right) // 2
            test_date = start_date + datetime.timedelta(days=mid - 1)
            
            if self.test_data_availability(test_date.year, test_date.month, test_date.day, inst_id):
                result_date = test_date
                left = mid + 1  # 继续向后查找更晚的日期
            else:
                right = mid - 1
        
        return result_date
    
    def fetch_missing_data_smart(self, start_year: int, end_year: Optional[int] = None, 
                                inst_id: Optional[str] = None) -> None:
        """智能检查并获取缺失的历史数据
        
        Args:
            start_year: 开始年份
            end_year: 结束年份，默认为当前年份
            inst_id: 交易对，默认为类初始化时的default_inst_id
        """
        # 使用默认交易对如果未提供
        inst_id = inst_id or self.default_inst_id
        end_year = end_year or datetime.date.today().year
        
        print(f"🚀 开始智能检测 {inst_id} 数据范围...")
        
        # 1. 首先检测数据的实际可用范围
        earliest_date, latest_date = self.find_data_range(start_year, end_year, inst_id)
        
        if earliest_date is None or latest_date is None:
            print(f"❌ {inst_id} 在 {start_year}-{end_year} 年间没有可用数据")
            return
        
        print(f"📊 {inst_id} 数据可用范围: {earliest_date} 至 {latest_date}")
        
        # 2. 在实际可用范围内检查缺失的数据
        missing_dates = self.check_missing_data(
            earliest_date.strftime('%Y-%m-%d'), 
            latest_date.strftime('%Y-%m-%d'), 
            inst_id
        )
        
        if not missing_dates:
            print("✅ 在可用范围内所有数据文件都已存在，无需获取新数据")
            return
        
        print(f"发现 {len(missing_dates)} 天的数据缺失，开始获取...")
        
        # 显示缺失的日期范围（前5个和后5个）
        if len(missing_dates) <= 10:
            missing_str = ", ".join([d.strftime("%Y-%m-%d") for d in missing_dates])
        else:
            first_5 = ", ".join([d.strftime("%Y-%m-%d") for d in missing_dates[:5]])
            last_5 = ", ".join([d.strftime("%Y-%m-%d") for d in missing_dates[-5:]])
            missing_str = f"{first_5} ... {last_5}"
        
        print(f"缺失日期: {missing_str}")
        
        # 3. 获取缺失的数据
        success_count = 0
        error_count = 0
        
        # 使用tqdm创建进度条
        with tqdm(missing_dates, desc=f"获取缺失的 {inst_id} 数据", 
                 unit="天", ncols=100, colour='yellow') as pbar:
            
            for date in pbar:
                year, month, day = date.year, date.month, date.day
                filename = f'./datas/{inst_id}/{year:04d}/{month:02d}/{year:04d}{month:02d}{day:02d}.csv'
                
                # 更新进度条描述
                pbar.set_description(f"获取 {year}-{month:02d}-{day:02d}")
                
                try:
                    pbar.set_postfix(状态="获取中", 成功=success_count, 失败=error_count)
                    data = self.fetch_data_for_day(year, month, day, inst_id)
                    
                    # 确保目录存在
                    os.makedirs(os.path.dirname(filename), exist_ok=True)
                    
                    # 保存数据
                    data.to_csv(filename, index=False)
                    success_count += 1
                    pbar.set_postfix(状态="完成", 成功=success_count, 失败=error_count)
                    
                except DataValidationError as e:
                    error_count += 1
                    pbar.set_postfix(状态="校验失败", 成功=success_count, 失败=error_count)
                    error_msg = str(e)
                    tqdm.write(f"数据校验失败 {year}-{month:02d}-{day:02d}: {error_msg}")
                    # 记录到日志文件
                    self._log_error(inst_id, year, month, day, "数据校验失败", error_msg)
                    
                    # 如果是当天或未来日期的错误，退出程序
                    if "无法获取当天或未来日期的完整数据" in error_msg:
                        tqdm.write(f"\n检测到当天或未来日期，停止数据获取。")
                        tqdm.write(f"最后处理日期: {year}-{month:02d}-{day:02d}")
                        break
                    continue
                except Exception as e:
                    error_count += 1
                    pbar.set_postfix(状态="获取失败", 成功=success_count, 失败=error_count)
                    error_msg = str(e)
                    tqdm.write(f"获取数据失败 {year}-{month:02d}-{day:02d}: {error_msg}")
                    # 记录到日志文件
                    self._log_error(inst_id, year, month, day, "数据获取失败", error_msg)
                    continue
        
        print(f"\n缺失数据获取完成！")
        print(f"成功: {success_count} 天")
        print(f"失败: {error_count} 天")
        print(f"总计处理: {len(missing_dates)} 天")
        print(f"数据范围: {earliest_date} 至 {latest_date}")
    


def get_trading_instruments(flag: str = "0", inst_type: str = "SWAP", filter_suffix: str = "-USDT") -> List[str]:
    """获取交易产品基础信息
    
    Args:
        flag: 交易环境标识，实盘为"0"，模拟盘为"1"
        inst_type: 产品类型，如"SWAP"、"SPOT"等
        filter_suffix: 过滤后缀，如"-USDT"
        
    Returns:
        符合条件的交易对列表
    """
    import okx.PublicData as PublicData
    
    try:
        publicDataAPI = PublicData.PublicAPI(flag=flag)
        
        # 获取交易产品基础信息
        result = publicDataAPI.get_instruments(instType=inst_type)
        
        if not result or 'data' not in result:
            print(f"获取交易产品信息失败: {result}")
            return []
        
        # 过滤出符合条件的交易对
        inst_id_list = [item['uly'] for item in result['data'] 
                       if filter_suffix in item['uly']]
        
        print(f"获取到 {len(inst_id_list)} 个 {inst_type} 类型的 {filter_suffix} 交易对")
        return inst_id_list
        
    except Exception as e:
        print(f"获取交易产品信息时出错: {str(e)}")
        return []


if __name__ == "__main__":
    flag = "0"  # 实盘:0 , 模拟盘：1
    
    # 获取交易产品基础信息
    inst_id_list = get_trading_instruments(
        flag=flag,
        inst_type="SWAP",
        filter_suffix="-USDT"
    )
    
    if not inst_id_list:
        print("未获取到任何交易对，程序退出")
        exit(1)
    
    print(f"将处理以下交易对: {', '.join(inst_id_list[:5])}{'...' if len(inst_id_list) > 5 else ''}")

    for inst_id in inst_id_list:
        data_fetcher = OKXDataFetcher(
            default_inst_id=inst_id
        )
                
        # 智能检查并获取历史数据
        print(f"🚀 开始智能获取 {inst_id} 历史数据...")
        data_fetcher.fetch_missing_data_smart(
            start_year=2010,  # 从2010年开始检测
            end_year=None,    # None表示到当前年份
            inst_id=inst_id
        )
            
            